#include "AudioDecode.h"
#include "FmtCvt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define AUDIO_REFILL_THRESH 4096

int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    AudioDecode* dec = (AudioDecode*)opaque;
    int ret = 0;
    while(0==ret)
    {
        ret = dec->m_ring->read_chunk((char*)buf, buf_size);
        if(0==ret){
            usleep(10000);
        }
    }

    if(ret<0)
    {
        printf("read_packet err %d\n", ret);
    }
    return ret;
};

AudioDecode::AudioDecode(int out_sample_rate):
    m_codec(NULL),
    m_codec_ctx(NULL),
    m_fmt_ctx(NULL),
    m_pkt(NULL),
    m_frm(NULL),
    m_swr(NULL),
    m_buffer(NULL),
    m_buffer_size(0),
    m_buffer_capacity(32000),
    m_is_inited(false),
    m_is_end(false)
{
    m_swr = new FmtCvt(AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_S16, out_sample_rate);
    m_ring = new RingBufferBytes(320000);
    m_buffer = (uint8_t*)malloc(m_buffer_capacity);

    av_register_all();
    //avcodec_register_all();
}

AudioDecode::~AudioDecode()
{
    destroy();
    delete m_swr;
    delete m_ring;
    if(NULL!=m_buffer)
    {
        free(m_buffer);
        m_buffer = NULL;
    }
}

int AudioDecode::init()
{
    if(m_is_inited)
    {
        return -1;
    }

    //1.注册组件
    //av_register_all();
    //封装格式上下文
    m_fmt_ctx = avformat_alloc_context();

    int bufSize = 4096;
    m_avio_buffer = (unsigned char*)av_malloc((size_t)(bufSize));

    m_fmt_ctx->pb = avio_alloc_context(m_avio_buffer, bufSize, 0, this, read_packet, NULL, NULL);
    m_fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
    //m_fmt_ctx->probesize = 4096;
    //m_fmt_ctx->max_analyze_duration = 1000;

    //2.打开输入音频文件
    if (avformat_open_input(&m_fmt_ctx, NULL, NULL, NULL) != 0) {
        fprintf(stderr, "%s\n", "打开输入音频文件失败");
        destroy();
        return -1;
    }
    //3.获取音频信息
    if (avformat_find_stream_info(m_fmt_ctx, NULL) < 0) {
        fprintf(stderr, "%s\n", "获取音频信息失败");
        destroy();
        return -1;
    }

    av_dump_format(m_fmt_ctx, 0, 0, 0);

    //音频解码，需要找到对应的AVStream所在的m_fmt_ctx->streams的索引位置
    m_audio_stream_idx = -1;
    int i = 0;
    for (; i < m_fmt_ctx->nb_streams; i++) {
        //根据类型判断是否是音频流
        if (m_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            m_audio_stream_idx = i;
            break;
        }
    }
    //4.获取解码器
    //根据索引拿到对应的流,根据流拿到解码器上下文
    m_codec_ctx = m_fmt_ctx->streams[m_audio_stream_idx]->codec;
    //再根据上下文拿到编解码id，通过该id拿到解码器
    m_codec = avcodec_find_decoder(m_codec_ctx->codec_id);
    printf("codec_id = 0x%x\n", m_codec_ctx->codec_id);
    if (m_codec == NULL) {
        fprintf(stderr, "%s\n", "无法解码");
        destroy();
        return -2;
    }
    //5.打开解码器
    if (avcodec_open2(m_codec_ctx, m_codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec, codec_id = 0x%x \n", m_codec->id);
        destroy();
        return -4;
    }

    if (!(m_pkt = av_packet_alloc())) {
        fprintf(stderr, "Could not allocate audio packet\n");
        destroy();
        return -6;
    }
    av_init_packet(m_pkt);

    if (!(m_frm = av_frame_alloc())) {
        fprintf(stderr, "Could not allocate audio frame\n");
        destroy();
        return -7;
    }

    m_is_inited = true;
    m_is_end = false;
    printf("init ok\n");
    return 0;
}

int AudioDecode::destroy()
{
    printf("destroy in\n");
    m_swr->exit();
    //if(!m_is_inited)
    //{
        //return 0;
    //}
    if(m_codec_ctx)
    {
        avcodec_close(m_codec_ctx);
        m_codec_ctx = NULL;
    }
    if(m_frm)
    {
        av_frame_free(&m_frm);
        av_free(m_frm);
        m_frm = NULL;
    }
    if(m_pkt)
    {
        av_packet_free(&m_pkt);
        m_pkt = NULL;
    }
    if(m_fmt_ctx)
    {
        if (m_fmt_ctx->pb)
        {
            av_free(m_fmt_ctx->pb->buffer);
            av_free(m_fmt_ctx->pb);
        }
        avformat_close_input(&m_fmt_ctx);
        m_fmt_ctx = NULL;
    }
    printf("destroy ok\n");
    m_is_inited = false;
    return 0;
}

int AudioDecode::send_data(const uint8_t* data, int data_size)
{
    return m_ring->write((const char*)data, data_size);
}

int AudioDecode::decode(uint8_t** out_pptr)
{
    int ret = init();
    if(!m_is_inited)
    {
        return ret;
    }

    m_buffer_size = 0;
    *out_pptr = m_buffer;

    ret = av_read_frame(m_fmt_ctx, m_pkt);
    if ( ret >= 0) {
        if (m_pkt->stream_index == m_audio_stream_idx)
        {
            ret = _decode(m_codec_ctx, m_pkt, m_frm);
            //ret = _decode_old(m_codec_ctx, m_pkt, m_frm);
        }
    }else if(-1==ret){
        fprintf(stderr, "av_read_frame read over, ret = %d\n", ret);
    }else if(AVERROR_EOF==ret){
        ret = -1;
        fprintf(stderr, "av_read_frame read eof, ret = %d\n", ret);
    }else {
        fprintf(stderr, "Error av_read_frame, ret = %d\n", ret);
    }
    av_packet_unref(m_pkt);
    if(ret < 0) {
        stop();
        if(!m_is_end)
        {
            m_is_end = true;

            //flush buffer
            uint8_t* out_buffer;
            int size = m_swr->flush(&out_buffer);
            if(size>0)
            {
                if((m_buffer_size+size) > m_buffer_capacity)
                {
                    m_buffer_capacity = m_buffer_size+size;
                    m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_capacity);
                }
                memcpy(m_buffer+m_buffer_size, out_buffer, size);
                m_buffer_size += size;
                //printf("flush buffer size = %d\n", m_buffer_size);
                return m_buffer_size;
            }

        }
        return ret;
    }

    return m_buffer_size;
}

int AudioDecode::_decode_old(AVCodecContext *dec_ctx, AVPacket *packet, AVFrame *frame)
{
    int ret, got_frame;
    //解码AVPacket->AVFrame
    ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, packet);
    if (ret < 0) {
        return ret;
    }
    if (got_frame) {
        uint8_t* out_buffer;
        //m_swr->init(dec_ctx);
        int size = m_swr->convert(frame, &out_buffer);
        if(size>0)
        {
            if((m_buffer_size+size) > m_buffer_capacity)
            {
                m_buffer_capacity = m_buffer_size+size;
                m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_capacity);
            }
            memcpy(m_buffer+m_buffer_size, out_buffer, size);
            m_buffer_size += size;
        }
        av_frame_unref(frame);
    }
    return ret;
}

int AudioDecode::_decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame)
{
    /* 发送编码数据包给解码器 */
    int ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){
        fprintf(stderr, "Error submitting the packet to the decoder, ret = %d\n", ret);
        return 0;
    }

    /* 读取所有的输出帧*/
    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return 0;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            return ret;
        }
        int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
        if (data_size < 0) {
            /* 不应该出现的情况 */
            fprintf(stderr, "Failed to calculate data size\n");
            return -4;
        }

        if (frame->channels > 0 && frame->channel_layout == 0)
            frame->channel_layout = av_get_default_channel_layout(frame->channels);
        else if (frame->channels == 0 && frame->channel_layout > 0)
            frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);

        //printf("channel_num : %d\n", av_get_channel_layout_nb_channels(frame->channel_layout));
        //printf("sample_fmt : %s\n", av_get_sample_fmt_name((AVSampleFormat)frame->format));
        //printf("bytes_per_sample : %d\n", av_get_bytes_per_sample((AVSampleFormat)frame->format));
        //printf("sample_rate : %d\n", frame->sample_rate);

        uint8_t* out_buffer;
        int size = m_swr->convert(frame, &out_buffer);
        if(size>0)
        {
            if((m_buffer_size+size) > m_buffer_capacity)
            {
                m_buffer_capacity = m_buffer_size+size;
                m_buffer = (uint8_t*)realloc(m_buffer, m_buffer_capacity);
            }
            memcpy(m_buffer+m_buffer_size, out_buffer, size);
            m_buffer_size += size;
        }

        av_frame_unref(frame);
    }
    return 0;
}

int AudioDecode::start()
{
    m_ring->open();
    return 0;
}

int AudioDecode::stop()
{
    m_ring->close();
    return 0;
}

